//! GUI 模块数据
//! GUI没帧刷新都需要执行调用，更新面板数据，方便随时读取
//! 目前的想法
//! 目前gui采用的electron 使用js来调用Rust的代码
//! 为了保证不出现数据竞争和减少所开销，js的GUI线程应该持有属于自己一份数据和一个channel
//! Rust代码通过发送给channel消息来进行GUI数据的更新
//! 每次GUI数据调用模块方法
//! ```no_run
//! update_gui_data(receiver_ptr: *const Receiver<Envelop>, data_ptr: *mut GUIData)
//! ```
//! GUI线程就会处理这段期间挤压的消息来更新数据，然后js通过对数据读取来进行展示
//!
//! 其他协程书写数据方法
//! ```no_run
//! use crate::gui::{GUIData, GUIDataHandler, Envelop};
//! use std::sync::mpsc::channel;
//!
//! // 想要一个接口
//! // 消息只要实现这个接口，写好如何更新gui数据即可
//! // 然后发送给gui channel 就可以了
//!
//! struct TestMsg {}
//!
//! impl GUIDataHandler for TestMsg {
//!     fn handle(&self, data: &mut GUIData) {
//!         data.num += 1;
//!     }
//! }
//!
//!
//! ```
//!

use std::sync::mpsc::Receiver;

#[repr(C)]
pub struct GUIData {
    pub num: u32,
}

// 包装
pub struct Envelop(Box<dyn GUIDataHandler + Send>);

impl Envelop {
    fn handle(&self, data: &mut GUIData) {
        self.0.handle(data);
    }
}

impl<T: GUIDataHandler + 'static + Send> From<T> for Envelop {
    fn from(src: T) -> Self {
        let b = Box::new(src);
        Envelop(b)
    }
}

/// gui数据处理接口
pub trait GUIDataHandler {
    fn handle(&self, data: &mut GUIData);
}

/// 更新gui数据的接口
/// gui线程可以调用这个接口来更新数据
#[no_mangle]
pub extern "C" fn update_gui_data(receiver_ptr: *const Receiver<Envelop>, data_ptr: *mut GUIData) {
    let receiver = unsafe {
        &*receiver_ptr
    };
    let data = unsafe {
        &mut *data_ptr
    };
    while let Ok(mut msg) = receiver.try_recv() {
        // 处理消息
        msg.handle(data);
    }
}

